#include <windows.h>
#include "SwfInterface.h"
#pragma comment(lib,"FlashRuntimeExtensions")
#include "ExtendModule.h"
#include "DynamicCodeLoader.h"
#include "AneJSON.h"
#ifdef _DEBUG
extern "C"
{
	extern void OpenConsoleWindow();
	extern void CloseConsoleWindow();
};
#define LOGI printf 
#else
#define LOGI
#endif

typedef struct
{
	int native;
	FREObject obj;
}JSONCB_Data;

FRENamedFunction* func=NULL;
FREContext g_ctx;
CExtendModule m_extendModule;


int JSONObject2AS(JSONObject jsobj, FREObject obj);
#if 0
DLL void	WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	LOGI("%x =message=%d \n",hwnd, message);
	//return 0;
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	LOGI("hModule.%p lpReserved.%p \n", hModule, lpReserved);

	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		LOGI("Process attach. \n");
		break;

	case DLL_PROCESS_DETACH:
		LOGI("Process detach. \n");
		break;

	case DLL_THREAD_ATTACH:
		LOGI("Thread attach. \n");
		break;

	case DLL_THREAD_DETACH:
		LOGI("Thread detach. \n");
// 		{
// 			MSG      msg ;  
// 			GetMessage (&msg, NULL, 0, 0);
// 			{  
// 				TranslateMessage (&msg) ;  
// 				DispatchMessage (&msg) ;  
// 			}  
// 		}
		break;
	}

	return (TRUE);
}
#endif

void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, 
						uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet)
{
	unsigned char funcNum= 1;
	*numFunctionsToTest = funcNum;
	func = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)*funcNum);

	func[0].name = (const uint8_t*)"callCommand";
	func[0].functionData = NULL;
	func[0].function = &callCommand;

	g_ctx = ctx;
	*functionsToSet = func;
}

void ContextFinalizer(FREContext ctx) {
#if _DEBUG
CloseConsoleWindow();
#endif
	free(func);
	return;
}

void ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet) 
{
	*extDataToSet = NULL;
	*ctxInitializerToSet = ContextInitializer;
	*ctxFinalizerToSet = ContextFinalizer;
#if _DEBUG
	OpenConsoleWindow();
#endif
}


void ExtFinalizer(void* extData) {


	return;
}

FREObject callCommand(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
	FREObject ret=NULL;
	const char *commandName;
	uint32_t plen;

	FREGetObjectAsUTF8(argv[0],&plen, (const uint8_t **)&commandName);
	if(strcmp(commandName, "RegisterModule") == 0)
	{
		 char *moduleName = NULL;
		 char *modulePath = NULL;
		 char **apilist = NULL;
		int apinum = 0, i;
		uint32_t arrlength;

		FREGetObjectAsUTF8(argv[1],&plen, (const uint8_t **)&moduleName);
		
		if (!moduleName)
		{
			goto err;
		}
		FREGetObjectAsUTF8(argv[2],&plen, (const uint8_t **)&modulePath);
		if (!modulePath)
		{
			goto err;
		}
		FREGetObjectAsInt32(argv[3], &apinum);
		if (apinum <= 0)
		{
			goto err;
		}
		//FREGetArrayLength(argv[4], &arrlength);


		apilist = new char*[apinum];
		FREObject api = argv[4];
		for (i = 0; i < apinum; ++ i)
		{
			char *apiname = NULL;
			
			FREObject tmp;
			FREGetArrayElementAt(api,i,&tmp);
			
			FREGetObjectAsUTF8(tmp,&plen, (const uint8_t **)&apiname);
			
			if (apiname)
			{
				apilist[i] = apiname;
			}
			else
			{
				apilist[i] = NULL;
			}
		}

		if (strncmp(modulePath, "file://", 7) == 0)
		{
			m_extendModule.RegisterModule(modulePath + 7, moduleName, apilist, apinum);
		}
		else
		{
			m_extendModule.RegisterModule(modulePath, moduleName, apilist, apinum);
		}
		
		
		delete [] apilist;
	}
	else if (strcmp(commandName, "ReleaseModule") == 0)
	{
		const uint8_t *moduleName;
		FREGetObjectAsUTF8(argv[1],&plen, &moduleName);
		m_extendModule.ReleaseModule((char*)moduleName);
	}
	else
	{
		char *api = NULL;
		char *params = NULL;
		void *res = NULL;

		FREGetObjectAsUTF8(argv[1],&plen, (const uint8_t**)&api);
		if (!api)
		{
			goto err;
		}
		FREGetObjectAsUTF8(argv[2],&plen, (const uint8_t**)&params);
		if (!params)
		{
			goto err;
		}

		res = m_extendModule.CallAPI((char*)commandName, api, params);
		
		if(res)
		{
			//uint32_t reslen = strlen((const char*)res);
			// FRENewObjectFromUTF8(reslen, (const uint8_t *)res, &ret);
			FRENewObject((const uint8_t*)"Object",0,NULL,&ret,NULL);
			JSONObject2AS(res,ret);
		}
		
	}

err:
	return ret;
}

void sendMessageSwf(const char* name, const char* value)
{
	FREDispatchStatusEventAsync(g_ctx,(const uint8_t*)name,(const uint8_t*)value);
	/*JSONObject jobj=NULL;
	CJSONParser jp;

	jobj = jp.ParseJSON((char*)value);
	FREObject ret=NULL;
	FRENewObject((const uint8_t*)"Object",0,NULL,&ret,NULL);
	JSONObject2AS(jobj, ret);*/
}


static void onJSONArrayFind(AneVal *val, void *userdata);

static void onJSONObjectFind(AneVal *val, void *userdata)
{
	if (!val || !userdata)
	{
		return;
	}
	JSONCB_Data *jdata = (JSONCB_Data*)userdata;
	int* native = &(jdata->native);
	FREObject array = jdata->obj;
	uint32_t value;
	FREObject object;
	switch(val->type)
	{
	case ANE_json_type_boolean:		
		FRENewObjectFromBool  (val->val._boolean, &object );
		FRESetObjectProperty(array,(const uint8_t*)val->name,object,NULL);
		break;

	case ANE_json_type_double:
		FRENewObjectFromDouble( val->val._number_double, &object );
		FRESetObjectProperty(array,(const uint8_t*)val->name,object,NULL);
		break;

	case ANE_json_type_int:
		FRENewObjectFromInt32( val->val._number_int, &object ); 
		FRESetObjectProperty(array,(const uint8_t*)val->name,object,NULL);
		break;

	case ANE_json_type_object:
		{
			FRENewObject((const uint8_t*)"Object",0,NULL,&object,NULL);
			JSONCB_Data cbdata;
			cbdata.native = 0;
			cbdata.obj = object;
			FRESetObjectProperty(array,(const uint8_t*)val->name,object,NULL);
			ANEJSON_SearchObject(val->val._object, onJSONObjectFind, &cbdata);
			
		}
		break;

	case ANE_json_type_array:
		{
			FRENewObject((const uint8_t*)"Array", 0, NULL, &object, NULL);
			int len = ANEJSON_GetArray(val->val._array);
			FRESetArrayLength(object, len);
			JSONCB_Data cbdata;
			cbdata.native = 0;
			cbdata.obj = object;
			FRESetObjectProperty(array,(const uint8_t*)val->name,object,NULL);
			ANEJSON_SearchArray(val->val._array, onJSONArrayFind, &cbdata);
			
		}
		break;

	case ANE_json_type_string:
		{
			int slen = strlen(val->val._string);
		FRENewObjectFromUTF8(slen,(const uint8_t*)val->val._string,&object);
		FRESetObjectProperty(array,(const uint8_t*)val->name,object,NULL);
		}
		break;

	default:
		break;
	}
}

static void onJSONArrayFind(AneVal *val, void *userdata)
{
	
	if (!val || !userdata)
	{
		return;
	}

	JSONCB_Data* jdata = (JSONCB_Data*)userdata;
	int native = jdata->native;
	FREObject array = jdata->obj;
	uint32_t value;
	FREObject object;
	switch(val->type)
	{
	case ANE_json_type_boolean:
		FRENewObjectFromBool  (val->val._boolean, &object );
		FRESetArrayElementAt(array,native ,object);
		native ++;
		//ArrPropertyPushNumber(array, !(!val->val._boolean));
		break;

	case ANE_json_type_double:
		FRENewObjectFromDouble( val->val._number_double, &object );
		FRESetArrayElementAt(array,native ,object);
		native ++;
		break;

	case ANE_json_type_int:
		FRENewObjectFromInt32( val->val._number_int, &object ); 
		FRESetArrayElementAt(array,native ,object);
		native ++;
		break;

	case ANE_json_type_object:
		{
			
			FRENewObject((const uint8_t*)"Object",0,NULL,&object,NULL);
			JSONCB_Data cbdata;
			cbdata.native = 0;
			cbdata.obj = object;
			FRESetArrayElementAt(array,native ,object);
			native ++;
			ANEJSON_SearchObject(val->val._object, onJSONObjectFind, &cbdata);
			
		}
		break;

	case ANE_json_type_array:
		{
			
			FRENewObject((const uint8_t*)"Array", 0, NULL, &object, NULL);
			int len = ANEJSON_GetArray(val->val._array);
			FRESetArrayLength(object, len);
			JSONCB_Data cbdata;
			cbdata.native = 0;
			cbdata.obj = object;
			FRESetArrayElementAt(array,native ,object);
			native ++;
			ANEJSON_SearchArray(val->val._array, onJSONArrayFind, &cbdata);
			
		}
		break;

	case ANE_json_type_string:
		{
			int slen = strlen(val->val._string);
		FRENewObjectFromUTF8(slen,(const uint8_t*)val->val._string,&object);
		FRESetArrayElementAt(array,native ,object);
		native ++;
		
		}
		break;

	default:
		break;
	}
	jdata->native = native;
}

int JSONObject2AS(JSONObject jsobj, FREObject obj)
{	
	JSONCB_Data userdata;

	userdata.native = 0;
	userdata.obj = obj;
	ANEJSON_SearchObject(jsobj,onJSONObjectFind, &userdata);
	return 1;
}